/*
 * jusb.h
 *
 *  Created on: 21.02.2009
 *      Author: fregat
 */

#ifndef JUSB_H_
#define JUSB_H_

# include <string>
# include "usb_utility.h"
# include "usb_interface.h"
# include "fregat_microchip_usb_jusb.h"
# include <iostream>
# include <boost/smart_ptr.hpp>

namespace fregat {
namespace microchip {
namespace usb {

typedef std::string class_name_t;
typedef std::string method_name_t;
typedef std::string field_name_t;
typedef enum {initial, opened, closed, error_ } channel_state_t;

typedef struct {
	usb_device_descriptor_t			id;
	vendor_t						vid;
	productid_t						pid;
	device							*pd;
	inpoint_bulk					*pinp;
	outpoint_bulk					*outp;
	channel_state_t					state;
} usb_channel_t;


class busCollector;

class jniInterface : public logInterface, public exceptionInterface {
	JNIEnv *penv;
	jobject jo;
protected :
	void doWriteLog (jLogger *, logLevel_t, std::string);
	void doThrowException (jException *, exceptionInterface::exceptionID_t, std::string);
public :
	jniInterface (JNIEnv *, jobject );

//	jniInterface ()
//	{
//		penv = getJNIEnv ();
//	}

	void setIntField (jobject jo, field_name_t fn, int value)
	{
		jclass jc = penv->GetObjectClass(jo);
		jfieldID fid = penv->GetFieldID(jc, fn.c_str(), "I");
		penv->SetIntField(jo, fid, value);
	}

	int getIntField (jobject jo, field_name_t fn)
	{
		//penv->getf
		jclass jc = penv->GetObjectClass(jo);
		jfieldID fid = penv->GetFieldID(jc, fn.c_str(), "I");
		return penv->GetIntField(jo, fid);
	}

	int getByteArrayLength (jbyteArray a)
	{
		return penv->GetArrayLength(a);
	}

	char *getByteArrayElement (jbyteArray a, int offset)
	{
		jboolean jb = JNI_FALSE;
		return (char *)penv->GetByteArrayElements(a,&jb);
	}

	jbyteArray getByteArray (std::vector <unsigned char> v)
	{
		jbyteArray a = penv->NewByteArray(v.size());
		//for (size_t i=0; i<v.size(); ++i) {
			//a[i] = 0; //(jbyte)v[i];
			penv->SetByteArrayRegion(a,0,v.size(),(jbyte *)&v[0]); //ObjectArrayElement(a, i, jbyte(v[i]));
		//}
		return a;
	}

	jobject createUsbDeviceObject (class_name_t cn, usb_device_descriptor_t i)
	{
		jclass jc = penv->FindClass (cn.c_str());
		jmethodID cid = penv->GetMethodID (jc, "<init>", "(I)V");
		return penv->NewObject (jc, cid, i);
	}
};


class usbEnvironment {
private :
	jniInterface	*pjniinterface;
	jLogger			*plogger;
	jException		*pexception;
	busCollector	*pbc;

	std::map <int, boost::shared_ptr <usb_channel_t> >			usb_channels;

	boost::shared_ptr <usb_channel_t> findChannel (vendor_t v, productid_t p);

	void collect_according_vendor (bus *, vendor_t, productid_t, std::list <device *> &);

public :
	typedef std::string class_name_t;
	usbEnvironment ();

	void
	update (JNIEnv *env, jobject jo)
	{
		std::cerr << "dada : " << __FILE__ << "." << __LINE__ << std::endl;
		pjniinterface = new jniInterface (env,jo);
		std::cerr << "dada : " <<  __FILE__ << "." << __LINE__ << std::endl;
	}

	void
	readConfiguration ();

	usb_device_descriptor_t
	findDevice (vendor_t v, productid_t p);

	int
	open_device (usb_device_descriptor_t);

	int
	write_device (const std::vector <unsigned char> &data, const usb_device_descriptor_t &d);

	void
	read_device (std::vector <unsigned char> &data, const usb_device_descriptor_t &d);


	boost::shared_ptr <usb_channel_t>
	findChannel(usb_device_descriptor_t d);

	void
	throwException (exceptionInterface::exceptionID_t eid, std::string s)
	{
		pjniinterface->throw_exception(pexception, eid, s);
	}

	void
	debug (std::string s)
	{
		pjniinterface->write_log(plogger,logInterface::DEBUG,s);
	}

	void
	error (std::string s)
	{
		pjniinterface->write_log(plogger,logInterface::FAULT,s);
	}

	void
	info (std::string s)
	{
		pjniinterface->write_log(plogger, logInterface::INFO,s);
	}

	jobject
	createUsbDeviceObject (class_name_t cn, usb_device_descriptor_t i)
	{
		return pjniinterface->createUsbDeviceObject(cn,i);
	}

	void
	setIntField (jobject jo, field_name_t fn, int value)
	{
		pjniinterface->setIntField (jo, fn, value);
	}

	int
	getIntField (jobject jo, field_name_t fn)
	{
		return pjniinterface->getIntField (jo, fn);
	}

	int
	getByteArrayLength (jbyteArray a)
	{
		return pjniinterface->getByteArrayLength (a);
	}

	char *
	getByteArrayElement (jbyteArray a, int offset)
	{
		return pjniinterface->getByteArrayElement (a, offset);
	}

	jbyteArray
	getByteArray (std::vector <unsigned char> v)
	{
		return pjniinterface->getByteArray(v);
	}
};


class jniBindings {
public :
	jniBindings (JNIEnv *penv, jobject jo);

	jobject
	createUsbDeviceObject (class_name_t cn, usb_device_descriptor_t i);

	void
	setIntField (jobject jo, field_name_t fn, int value);

	int
	getIntField (jobject jo, field_name_t fn);

	void
	throwException (exceptionInterface::exceptionID_t eid, std::string s);

	void
	debug (std::string s);

	void
	error (std::string s);

	void
	info (std::string s);

	void
	readConfiguration ();

	usb_device_descriptor_t
	findDevice (vendor_t v, productid_t p);

	boost::shared_ptr <usb_channel_t>
	findChannel(usb_device_descriptor_t);

	int
	openDevice (usb_device_descriptor_t);

	int
	writeDevice (const usb_device_descriptor_t &, const jbyteArray &);

	jbyteArray
	readDevice (const usb_device_descriptor_t &);
};

}
}
}


#endif /* JUSB_H_ */
